package com.ushine.web.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Optional;

/**
 * @Author yls
 * @Date 2020/3/30 17:03
 * @Description 性能测试 == 切面日志记录
 * 切面编程，Aspect注解表示这是一个切面类
 * @Version 1.0
 * <p>
 * JoinPoint包含了几个很有用的参数：
 * Object[] getArgs：返回目标方法的参数
 * Signature getSignature：返回目标方法的签名
 * Object getTarget：返回被织入增强处理的目标对象
 * Object getThis：返回AOP框架为目标对象生成的代理对象
 **/
@Aspect
@Component
@Slf4j
public class WebBusinessLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebBusinessLogAspect.class);

    /**
     * 扫描所有的【Controller】
     */
    private static final String TEMPLATE_CONTROLLER_AOP = "execution(* com.ushine.web.template..*Controller.*(..))";
    /**
     * 扫描所有的【Service】
     */
    private static final String TEMPLATE_SERVICE_AOP = "execution(* com.ushine.web.template..*ServiceImpl.*(..))";
    /**
     * 扫描所有的【FacadeImpl】
     */
    private static final String TEMPLATE_FACADE_SERVICE_AOP = "execution(* com.ushine.web.template..*FacadeImpl.*(..))";

    /**
     * 扫描所有的【Controller】
     */
    private static final String WEB_CONTROLLER_AOP = "execution(* com.ushine.web.web..*Controller.*(..))";
    /**
     * 扫描所有的【Service】
     */
    private static final String WEB_SERVICE_AOP = "execution(* com.ushine.web.web..*ServiceImpl.*(..))";
    /**
     * 扫描所有的【FacadeImpl】
     */
    private static final String WEB_FACADE_SERVICE_AOP = "execution(* com.ushine.web.web..*FacadeImpl.*(..))";


    /**
     * 功能描述:
     * 〈 AOP 的切入点，控制整个切面的执行方法〉
     *
     * @return : void
     * @author : yls
     * @date : 2022/1/24 15:39
     */
    @Pointcut(TEMPLATE_CONTROLLER_AOP + "||" + TEMPLATE_SERVICE_AOP + "||" + TEMPLATE_FACADE_SERVICE_AOP +
            "||" + WEB_CONTROLLER_AOP + "||" + WEB_SERVICE_AOP + "||" + WEB_FACADE_SERVICE_AOP)
    public void logAspect() {
        // 定义切点，没有具体的方法体
    }

    /**
     * 功能描述:
     * 〈 前置通知：在方法执行之前执行，通知用户该方法开始执行 〉
     *
     * @param point 对应的具体的切点
     * @return : void
     * @author : yls
     * @date : 2022/1/24 15:43
     */
    @Before(value = "logAspect()")
    public void before(JoinPoint point) {
        log.info("{}开始执行时间{}！", point.getSignature().getName(), System.currentTimeMillis());
    }


    /**
     * 功能描述:
     * 〈 后置通知：在切入点的业务逻辑执行完得时候执行，通知用户已经执行完成！ 〉
     *
     * @param point 切入点
     * @return : void
     * @author : yls
     * @date : 2022/1/24 15:44
     */
    @After(value = "logAspect()")
    public void after(JoinPoint point) {
        log.info("{}结束执行{}！", point.getSignature().getName(), System.currentTimeMillis());
    }


    /**
     * 功能描述:
     * 〈 后置通知：在返回结果集之后执行 〉
     *
     * @param point  切入点
     * @param result 结果集
     * @return : void
     * @author : yls
     * @date : 2022/1/24 15:57
     */
    @AfterReturning(value = "logAspect()", returning = "result")
    public void afterReturn(JoinPoint point, Object result) {
        result = Optional.ofNullable(result).orElse("{}");
        log.info("{}执行完成，返回的结果集为：{}", point.getSignature().getName(), result.toString());
    }

    /**
     * 功能描述:
     * 〈 抛出异常之后执行 〉
     *
     * @param point 切入点
     * @param e     具体的异常信息
     * @return : void
     * @author : yls
     * @date : 2022/1/24 16:03
     */
    @AfterThrowing(value = "logAspect()", throwing = "e")
    public void afterThrow(JoinPoint point, Exception e) {
        log.info("捕获业务异常:{}", e.getMessage());
    }


    /**
     * 功能描述:
     * 〈 环绕通知：监控方法从开始到结束的执行情况 〉
     *
     * @param proceedingJoinPoint 切入点
     * @return : void
     * @author : yls
     * @date : 2022/1/24 15:46
     */
    @Around(value = "logAspect()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 执行的方法名
        String name = proceedingJoinPoint.getSignature().toString();
        // 开始执行时间
        long startTime = System.currentTimeMillis();
        // 执行具体的业务逻辑
        Object proceed = proceedingJoinPoint.proceed();
        // 方法执行结束时间
        long endTime = System.currentTimeMillis();
        LOGGER.info("{}执行的耗费时间{}/ms", name, (endTime - startTime));
        return proceed;
    }


}
